/*
 * This code is made available under version 3 of the
 * GNU GENERAL PUBLIC LICENSE. See the file LICENSE in this
 * distribution for details.
 * 
 * Copyright 2008 Eric Bodden
 */

package infiniteloop1;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.aspectj.lang.JoinPoint.StaticPart;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.SourceLocation;

/**
 * Locking aspect, keeping track of thread-local lock sets.
 * 
 * @author Eric Bodden
 */
public aspect InfiniteLoop {
	public static int state=0;
	public static JoinPointCache joinpointCache=new JoinPointCache(1000);
	public static int loopThreshold=100;
	
	static class JoinPointCache{
		int [] cache=new int [1000];
		int size=1000;
		int num=0;
		public JoinPointCache(int size)
		{
			this.size=size;
		    cache=new int [size];
		    for (int i=0;i<size;i++)cache[i]=0;
		}
		
		public void add(int a)
		{
			for (int i=0;i<size-1;i++)cache[i]=cache[i+1];
			cache[size-1]=a;
			if(num<size)num++;
		}
		private int [] shiftright(int k)
		{
			int [] newCache=new int[size];
			for(int i=0;i<k;i++)
			{
				newCache[i]=cache[size-k+i];
			}
			for(int i=k;i<size;i++)
			{
				newCache[i]=cache[i-k];
			}
			return newCache;
		}
		public boolean hasLoop(int MaxLoopLength,int MaxLoopTime)
		{
			for (int k=1;k<=MaxLoopLength;k++)
			{
				int [] tmp=shiftright(k);
				int [] diff=new int[size];
				
				for(int i=0;i<size;i++)
				{
					diff[i]=cache[i]-tmp[i];
				}
				int state=0;
				int max0=0;
				System.out.println("cache:"+cache);
				System.out.println("====================");
				System.out.println("Diff:"+diff);
				
				for(int i=size-1;i>size-num-1;i--)
				{
					System.out.print(diff[i]+" ");
					if(diff[i]==0)
					{
						state++;
						if(state>max0)max0=state;
					}
					else
					{
						state=0;
					}
				}
				if(max0>MaxLoopTime*k)return true;
			}
			return false;
		}
	}
	
    pointcut traceMethods() : (!cflowbelow(call(* *(..)))
    		||get(* *.*)||set(* *.*)||lock()||unlock() )
    		&& !within(InfiniteLoop)&&!cflow(within(InfiniteLoop));
	   
	before(): traceMethods(){
	        Signature sig = thisJoinPointStaticPart.getSignature();
	        String line =""+ thisJoinPointStaticPart.getSourceLocation().getLine();
	        String sourceName = thisJoinPointStaticPart.getSourceLocation().getWithinType().getCanonicalName();
	        Logger.getLogger("Tracing").log(Level.INFO, 
	                "Call from " +  sourceName +" line " + line
	                    +" to " +sig.getDeclaringTypeName() + "." + sig.getName()
	        );
			if(isInfiniteLoop(thisJoinPointStaticPart))
			{
				String id = getId(thisJoinPointStaticPart);
				Class owner = thisJoinPointStaticPart.getSignature().getDeclaringType();		
				SourceLocation loc = location(thisJoinPointStaticPart);
				
				System.err.println("Infinite Loop!:"+id+" "+owner+"  location:"+loc);
				System.err.println("Would you break the loop? Y/N");
				
				//if(System.in.equals("Y"))break;
				while(true)
				{
					if(System.in.equals("Y"))System.exit(1);
				}				
			}
	    }

	
	ThreadLocal locksHeld = new ThreadLocal() {
		 protected synchronized Object initialValue() {
			 return new HashBag();
		 }
	};
	


	private String getId(StaticPart sp) {
		return sp.getSignature().toLongString().intern();
	}
	private SourceLocation location(StaticPart sp){
		return sp.getSourceLocation();
	}
	
	public boolean isInfiniteLoop(Object jps)
	{
		System.out.println(jps.hashCode());
		joinpointCache.add(jps.hashCode());
		
        if(joinpointCache.hasLoop(10, 100))
        {
        	return true;
        }
        return false;
	}
	
}
